﻿#include "childedit.h"
#include <QApplication>
#include <QFile>
#include <QTextStream>
#include <QFileInfo>
#include <QMessageBox>
#include <QFileDialog>
#include <QPushButton>
#include <QCloseEvent>
#include <QMenu>
#include <QPalette>
#include <QItemSelectionModel>
#include <QDebug>
#include <QTextOption>
#include <QAbstractItemView>
#include <QScrollBar>

int ChildEdit::sequenceNumber = 0;

ChildEdit::ChildEdit(QWidget *parent) :
    CodeEditor(parent)
{
    //在子窗口关闭时销毁这个类的对象
    setAttribute(Qt::WA_DeleteOnClose);
    isUntitled = true;
    //选中字符颜色设置
    QPalette p = palette();
    p.setColor(QPalette::Highlight, Qt::darkBlue);
    p.setColor(QPalette::HighlightedText, Qt::white);
    setPalette(p);
    setLineWrapMode(NoWrap);//不自动换行
    setTabStopWidth(30);    //默认缩进
    setAcceptDrops(false);  //关闭编辑器的拖入事件，将事件上传
    completerInit();
    installEventFilter(this);
}

ChildEdit::~ChildEdit()
{
//    disconnect(&fsWatcher,0,0,0);
}

//!新建文件操作
void ChildEdit::newFile()
{
    isUntitled = true;
    sequenceNumber++;
    curFile = tr("未命名文档%1.txt").arg(sequenceNumber);

    // 设置窗口标题，使用[*]可以在文档被更改后在文件名称后显示”*“号
    setWindowTitle(curFile + "[*]");

    //信号，内容有改变，槽，变为未保存状态
    connect(document(), SIGNAL(contentsChanged()),
            this, SLOT(documentWasModified_SLOT()));
}

//!用户保存操作
bool ChildEdit::save()
{
    if(isUntitled){ //如果文件未被保存过，则执行另存为操作,否则直接保存文件
        return saveAs();
    } else {
        return saveFile(curFile);
    }

}

//!文件另存为
bool ChildEdit::saveAs()
{
#if fileWatcherEnable
    //删除原监视路径
    for(int i = 0; fsWatcher.files().size(); i++){
        if(fsWatcher.files().at(i) == currentFile()){
            fsWatcher.removePath(fsWatcher.files().at(i));
        }
    }
#endif
    QString fileName = QFileDialog::getSaveFileName(this,
                                                    tr("另存为"),
                                                    curFile,
                                                    "文本文件 (*.txt);;所有文件 (*.*)");
    if (fileName.isEmpty()){
        return false;
    }

#if fileWatcherEnable
    //改变文件检测器的路径
    fsWatcher.addPath(fileName);
#endif
    qDebug()<<"save as"<<fileName;
    return saveFile(fileName);
}

//!文件保存
bool ChildEdit::saveFile(const QString &fileName)
{
    fsWatcher.removePath(fileName);
    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("多文档编辑器"),
                             tr("无法写入文件 %1:\n%2.")
                             .arg(fileName).arg(file.errorString()));
        return false;
    }

    QTextStream out(&file);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    out << toPlainText(); // 以纯文本文件写入
    QApplication::restoreOverrideCursor();//鼠标光标
    while(out.status() != QTextStream::Ok){
        if(out.status() == QTextStream::WriteFailed){
            return false;
        }
    }
    setCurrentFile(fileName);
#if fileWatcherEnable
    //外部修改提示部分
    for(int i = 0; fsWatcher.files().size(); i++){
        if(fsWatcher.files().at(i) == fileName){
            return true;
        }
    }

    connect(&fsWatcher, SIGNAL(fileChanged(QString)),
            this, SLOT(documentWatcher_Slot(QString)));
    fsWatcher.addPath(fileName);
//    qDebug()<<fsWatcher.files()<<fileName;
#endif
    return true;
}

//!加载文件
bool ChildEdit::loadFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("多文档编辑器"),
                             tr("无法读取文件 %1:\n%2.")
                             .arg(fileName).arg(file.errorString()));
        return false;//只读方式打开文件，出错则提示，并返回false
    }

    QTextStream in(&file);
    QApplication::setOverrideCursor(Qt::WaitCursor);//设置鼠标状态为等待状态
    setPlainText(in.readAll()); //读取文件的全部文本内容，并添加到编辑器中
    QApplication::restoreOverrideCursor(); // 恢复鼠标状态

    setCurrentFile(fileName); // 设置当前文件

    //信号，内容有改变，槽，变为未保存状态
    connect(document(), SIGNAL(contentsChanged()),
            this, SLOT(documentWasModified_SLOT()));
    //文档前进后退
    connect(document(), SIGNAL(contentsChanged()),
            this, SLOT(positionChange_Slot()));

#if fileWatcherEnable
    //外部修改提示部分
    for(int i = 0; fsWatcher.files().size(); i++){
        if(fsWatcher.files().at(i) == fileName){
            return true;
        }
    }
    fsWatcher.addPath(fileName);
    connect(&fsWatcher, SIGNAL(fileChanged(QString)),
            this, SLOT(documentWatcher_Slot(QString)),Qt::DirectConnection);
    qDebug()<<fsWatcher.files();
#endif
    return true;
}

//!设置当前文件
void ChildEdit::setCurrentFile(const QString &fileName)
{
    // canonicalFilePath()可以除去路径中的符号链接，“.”和“..”等符号
    curFile = QFileInfo(fileName).canonicalFilePath();

    isUntitled = false; // 文件已经被保存过了
    document()->setModified(false); // 文档没有被更改过
    setWindowModified(false); // 窗口不显示被更改标志

    setWindowTitle(getCurrentFileFromInfo() + "[*]");
//    qDebug()<<curFile;
}

//!从文件路径中提取文件名
QString ChildEdit::getCurrentFileFromInfo()
{
    return QFileInfo(curFile).fileName();
}

//!设置语法高亮
void ChildEdit::setupHighLightTool(bool open)
{
    if(open){
        highlighter.setDocument(document());
    }else{
        highlighter.setDocument(NULL);
    }
}

//!转到行
void ChildEdit::gotoLine(int lineNum)
{
    QTextBlock block = document()->findBlockByLineNumber(lineNum);
    if(block.isValid()){
        QTextCursor cursor=textCursor();
        cursor.setPosition(block.position());
        setTextCursor(cursor);
        ensureCursorVisible();
        centerCursor();
    }else{
        block = document()->lastBlock();
        QTextCursor cursor=textCursor();
        cursor.setPosition(block.position());
        setTextCursor(cursor);
        ensureCursorVisible();
    }
}
//!前进后退功能
void ChildEdit::positisonMove(bool dir)
{
    if(dir){//back
        gotoLine(positionQue.back());
    }else{//next
        int tmp = positionQue.next();
        if(textCursor().blockNumber() == tmp)
            gotoLine(positionQue.next());
        else
            gotoLine(tmp);
    }
}
//!自动补全功能初始化
void ChildEdit::completerInit()
{
    c = new QCompleter(this);
    word_list<<tr("定义变量")<<tr("定时器")<<tr("调用子程序")
            <<tr("清空界面")<<tr("子程序")<<tr("跳转到")
            <<tr("按键 ,X ,Y ,L ,H ,颜色,\"\"")
            <<tr("显示矩形块X ,Y ,X ,Y ,颜色 ")
            <<tr("显示矩形框 X , Y , X , Y , 线粗 , 颜色 ")
            <<tr("显示文字 \"\" ,X , Y , 颜色 ")
            <<tr("显示数值 ,X , Y , 颜色 ")
            <<tr("显示图片 ,X , Y ")
            <<tr("显示实心圆X ,Y ,半径 , 颜色 ")
            <<tr("显示圆形X ,Y ,半径 ,线粗 ,颜色 ")
            <<tr("显示圆弧X ,Y ,半径 , 度, 度,线粗 ,颜色 ")
            <<tr("显示扇形X ,Y ,半径 , 度, 度,颜色 ")
            <<tr("显示直线X ,Y ,X ,Y ,线粗 ,颜色 ")
            <<tr("编辑框 ,X ,Y ,L ,H ")
            <<tr("写编辑框 , ")<<tr("读编辑框 , ")
            <<tr("直线X ,Y ,Z ,U ,速度 ")
            <<tr("顺圆弧X ,Y ,半径 ,速度 ")
            <<tr("逆圆弧X ,Y ,半径 ,速度 ")
            <<tr("调用G文件 ")
            <<tr("暂停运动")<<tr("继续运动")<<tr("停止运动")<<tr("回零")
            <<tr("相对坐标编程")<<tr("绝对坐标编程")
            <<tr("坐标偏移X ,Y ,Z ,U ")
            <<tr("顺旋转 ,速度 ")<<tr("逆旋转 ,速度 ")<<tr("停止旋转 ")
            <<tr("往复 ,速度 ")<<tr("开启AI调速")<<tr("关闭AI调速")
            <<tr("关闭限位 ")<<tr("开启限位 ")<<tr("开启定时器")<<tr("关闭定时器")
            <<tr("开启手轮 ")<<tr("关闭手轮 ")<<tr("DO = ")<<tr("AO = ")
            <<tr("串口发送 , ")<<tr("串口接收 ");
    word_list.sort();//注意对关键字列表排序
    string_list_model = new QStringListModel(word_list, this);
    c->setModel(string_list_model);
    c->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
    c->setCaseSensitivity(Qt::CaseInsensitive);
    c->setWrapAround(false);
    setCompleter(c);
}
//!设置补全对象
void ChildEdit::setCompleter(QCompleter *completer)
{
    if (c)
        QObject::disconnect(c, 0, this, 0);

    c = completer;

    if (!c)
        return;

    c->setWidget(this);
    c->setCompletionMode(QCompleter::PopupCompletion);
    c->setCaseSensitivity(Qt::CaseInsensitive);
    QObject::connect(c, SIGNAL(activated(QString)),
                     this, SLOT(insertCompletion_Slot(QString)));
}

QCompleter *ChildEdit::completer() const
{
    return c;
}

//!获得光标所在的词
QString ChildEdit::textUnderCursor() const
{
    QTextCursor tc = textCursor();
    tc.select(QTextCursor::WordUnderCursor);
    return tc.selectedText();
}

//!文件编译后未保存状态
void ChildEdit::documentWasModified_SLOT()
{
    setWindowModified(document()->isModified());
}

//!关闭事件
void ChildEdit::closeEvent(QCloseEvent *event)
{
    if (maybeSave()) {
        event->accept();
        //如果当前文档已保存，通知需要保存路径
        if(QFileInfo(curFile).isFile() && QFileInfo(curFile).isAbsolute())
            emit sig_fileClose(curFile);
    } else {
        event->ignore();
    }
}

//!检查和询问是否需要保存
bool ChildEdit::maybeSave()
{
    if (document()->isModified()) { // 如果文档被更改过
        QMessageBox box;
        box.setWindowTitle(tr("正在关闭文档"));
        box.setText(tr("是否保存对“%1”的更改？")
                    .arg(getCurrentFileFromInfo()));
        box.setIcon(QMessageBox::Warning);

        QPushButton *yesBtn = box.addButton(tr("是(&Y)"), QMessageBox::YesRole);
        box.addButton(tr("否(&N)"),QMessageBox::NoRole);
        QPushButton *cancelBtn = box.addButton(tr("取消"), QMessageBox::RejectRole);

        box.exec(); //弹出对话框
        if (box.clickedButton() == yesBtn)
            return save();
        else if (box.clickedButton() == cancelBtn)
            return false;
    }
    return true;
}

//!右键菜单事件
void ChildEdit::contextMenuEvent(QContextMenuEvent *e)
{
    QMenu *menu = new QMenu;
    QAction *undo = menu->addAction(tr("撤销(&U)"),this,
                                    SLOT(undo()),QKeySequence::Undo);
    undo->setEnabled(document()->isUndoAvailable());
    QAction *redo = menu->addAction(tr("恢复(&R)"),this,
                                    SLOT(redo()),QKeySequence::Redo);
    redo->setEnabled(document()->isRedoAvailable());
    menu->addSeparator();
    QAction *cut = menu->addAction(tr("剪切(&T)"),this,
                                   SLOT(cut()),QKeySequence::Cut);
    cut->setEnabled(textCursor().hasSelection());
    QAction *copy = menu->addAction(tr("复制(&C)"),this,
                                    SLOT(copy()),QKeySequence::Copy);
    copy->setEnabled(textCursor().hasSelection());
    menu->addAction(tr("粘贴(&P)"),this,SLOT(paste()),QKeySequence::Paste);

    menu->addSeparator();
    QAction *select = menu->addAction(tr("全选"),this,
                                SLOT(selectAll()),QKeySequence::SelectAll);
    select->setEnabled(!document()->isEmpty());

    menu->exec(e->globalPos()); // 获取鼠标的位置，然后在这个位置显示菜单
    delete menu;
}
//!文件外部修改处理
void ChildEdit::documentWatcher_Slot(const QString &path)
{
    //父窗口为活动状态不提示
    if(qobject_cast<QWidget*>(this->parent())->isActiveWindow()){
        return;
    }

    disconnect(&fsWatcher, SIGNAL(fileChanged(QString)),
               this, SLOT(documentWatcher_Slot(QString)));

    QMessageBox::StandardButton rb = QMessageBox::question(this, tr("提示"),
                          "'"+path+"'"+tr("\n此文档在外部被修改，是否重新加载该文档?"),
                          QMessageBox::Yes | QMessageBox::No,
                          QMessageBox::Yes);
    if(rb == QMessageBox::Yes){
        loadFile(path);
    }
    connect(&fsWatcher, SIGNAL(fileChanged(QString)),
            this, SLOT(documentWatcher_Slot(QString)),Qt::DirectConnection);
}
//!文档被编辑后记录位置
void ChildEdit::positionChange_Slot()
{
    positionQue.push(textCursor().blockNumber());
}

//!按键事件过滤器
bool ChildEdit::eventFilter(QObject *, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        //ctrl+"/"
        if (keyEvent->key() == Qt::Key_Slash && (keyEvent->modifiers() & Qt::ControlModifier)){
            int startline = document()->findBlock(textCursor().selectionStart()).blockNumber();
            int endline = document()->findBlock(textCursor().selectionEnd()).blockNumber();
//            if(startline == endline)
            {
                //检查本身是否注释
                QTextCursor cursor=textCursor();
                cursor.movePosition(QTextCursor::StartOfLine);
                cursor.select(QTextCursor::LineUnderCursor);
                setTextCursor(cursor);
                QRegExp findslash = QRegExp("^[\\s|\\t]*//");
                qDebug()<<cursor.selectedText().replace(findslash, "");
                if(cursor.selectedText().indexOf(findslash) >= 0){
                    cursor.insertText(cursor.selectedText().replace(findslash, ""));
                }else if(startline == endline){
                    QTextCursor cursor=textCursor();
                    cursor.movePosition(QTextCursor::StartOfLine);
                    setTextCursor(cursor);
                    cursor.insertText("//");
                }
            }
//            for(startline)

            return true;
        }
    } else if (event->type() == QEvent::InputMethod) {//使用输入法
        QInputMethodEvent *keyEvent = dynamic_cast<QInputMethodEvent*>(event);
        static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
        QString completionPrefix = keyEvent->commitString();

        if (/*!isShortcut && (hasModifier || e->text().isEmpty()||*/ completionPrefix.length() < 2
                          /*|| eow.contains(completionPrefix.right(1))*/) {
            c->popup()->hide();
            return false;
        }
        //筛选列表
        if (completionPrefix != c->completionPrefix()) {
            c->setCompletionPrefix(completionPrefix);
            c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
        }
        // 弹出列表框
        QRect cr = cursorRect();
        cr.setWidth(c->popup()->sizeHintForColumn(0)
                    + c->popup()->verticalScrollBar()->sizeHint().width());
        c->complete(cr);
        return false;
    }
    return false;
}

void ChildEdit::focusInEvent(QFocusEvent *e)
{
    if (c)
        c->setWidget(this);
    CodeEditor::focusInEvent(e);
}

//!补全词
void ChildEdit::insertCompletion_Slot(const QString& completion)
{
    if (c->widget() != this)
        return;
    QTextCursor tc = textCursor();
    int extra = completion.length() - c->completionPrefix().length();
    tc.movePosition(QTextCursor::Left);
    tc.movePosition(QTextCursor::EndOfWord);
    tc.insertText(completion.right(extra));
    setTextCursor(tc);
}

void ChildEdit::keyPressEvent(QKeyEvent *e)
{
    if (c && c->popup()->isVisible()) {
        // The following keys are forwarded by the completer to the widget
       switch (e->key()) {
       case Qt::Key_Enter:
       case Qt::Key_Return:
       case Qt::Key_Escape:
       case Qt::Key_Tab:
       case Qt::Key_Backtab:
            e->ignore();
            return; // let the completer do default behavior
       default:
           break;
       }
    }

    bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_P); // CTRL+P
    if (!c || !isShortcut) // do not process the shortcut when we have a completer
        CodeEditor::keyPressEvent(e);

    const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier |Qt::BackButton);
    if (!c || (ctrlOrShift && e->text().isEmpty()))
        return;

    static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
    bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
    QString completionPrefix = textUnderCursor();

    if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
                      || eow.contains(e->text().right(1)))) {
        c->popup()->hide();
        return;
    }
    //筛选列表
    if (completionPrefix != c->completionPrefix()) {
        c->setCompletionPrefix(completionPrefix);
        c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
    }
    // 弹出列表框
    QRect cr = cursorRect();
    cr.setWidth(c->popup()->sizeHintForColumn(0)
                + c->popup()->verticalScrollBar()->sizeHint().width());
    c->complete(cr);
    //    qDebug()<<c->completionPrefix();
}

